package com.csx.base;

import com.alibaba.druid.pool.DruidDataSource;
import com.xxl.job.core.util.DateUtil;
import com.yh.csx.bsf.core.common.PropertyCache;
import com.yh.csx.bsf.core.db.DbConn;
import com.yh.csx.bsf.core.util.LogUtils;
import com.yh.csx.bsf.core.util.PropertyUtils;
import lombok.var;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 策略解析类
 */
public class StragtegyProvider implements AutoCloseable {
	/**
	 * 获取需要同步的数据库
	 * 
	 * @return
	 */
	public static volatile Map<String, Map<String, String>> DB_MAPS = new ConcurrentHashMap<String, Map<String, String>>();

	public static volatile Map<String, DataSource> DB_SOURCES = new ConcurrentHashMap<>();

	public static volatile Map<String,String> BLACK_TABLES = new ConcurrentHashMap<>();

	public static volatile Map<String,String> MysqlBLACK_TABLES = new ConcurrentHashMap<>();

	public StragtegyProvider(){
		PropertyCache.Default.listenUpdateCache("通过配置刷新上下文监听",(data)->{
			/**
			 * 配置修改未考虑并发锁获取配置信息的问题
			 */
			DB_MAPS.clear();
			DB_SOURCES.clear();
			BLACK_TABLES.clear();
			MysqlBLACK_TABLES.clear();
		});
	}
	public void close()
	{
		for(var key:DB_SOURCES.keySet())
		{
			try{
				var v = DB_SOURCES.get(key);
				if(v!=null)
				{
					if(v instanceof DruidDataSource)
					{
						((DruidDataSource)v).close();
					}
				}
			}
			catch (Exception exp){
				LogUtils.error(this.getClass(),Config.ProjectName,"释放druid连接池失败",exp);
			}
		}

	}

	public List<String> getDbs() {
		var array = Config.getCanalDbs().split(",");
		return Arrays.asList(array);
	}

	public String fullName(String db,String table){
		return db+"."+table;
	}

	public String parseDb(String fullTableName){
		if(fullTableName.contains(".")) {
			return fullTableName.split("\\.")[0];
		}
		return null;
	}

	public String parseTable(String fullTableName){
		if(fullTableName.contains(".")) {
			return fullTableName.split("\\.")[1];
		}else
		{
			return fullTableName;
		}
	}

	/**
	 * 根据数据库策略解析生成链接
	 *
	 * @return
	 */
	public DbConn getDb(String sourceDb) {
		return new DbConn(getDS(sourceDb));
	}
	
	/**
	 * 根据数据库策略解析生成链接
	 *
	 * @return
	 */
	public DbConn getDbByIP(String ip) {
		Map<String, String> info = getDbInfo(ip);
		if (info == null)
			return null;
		// 加载数据源连接池
		if (!DB_SOURCES.containsKey(ip)) {
			DataSource ds=createDataSource(ip,info);
			DB_SOURCES.put(ip, ds);
		}
		return new DbConn(DB_SOURCES.get(ip));
	}
	
	/**
	 * 获取数据源
	 * */
	public DataSource getDS(String sourceDb) {
		Map<String, String> info = getDbInfo(sourceDb);
		if (info == null)
			throw new RuntimeException("不能获取db连接信息:"+sourceDb);
		// 加载数据源连接池
		if (!DB_SOURCES.containsKey(sourceDb)) {
			DataSource ds=createDataSource(sourceDb,info);
			DB_SOURCES.put(sourceDb, ds);
		}
		return DB_SOURCES.get(sourceDb);
	}
	/**
	 * 创建数据源
	 * */
	private DruidDataSource createDataSource(String db, Map<String, String> dbInfo) {
		DruidDataSource ds = new DruidDataSource();
		ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
		ds.setUrl("jdbc:mysql://" + dbInfo.get("address") + "/" + db + "?zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false");
		ds.setUsername(dbInfo.get("username"));
		ds.setPassword(dbInfo.get("password"));
		ds.setInitialSize(0);
		ds.setMinIdle(0);
		ds.setMaxActive(20);
		ds.setMaxWait(120000);
		ds.setTestWhileIdle(true);
		ds.setValidationQuery("select 1;");
		ds.setValidationQueryTimeout(5000);
		ds.setTimeBetweenEvictionRunsMillis(60000);
		ds.setMinEvictableIdleTimeMillis(300000);
		try {
			ds.init();
		} catch (SQLException e) {
			ds.close();
			throw new RuntimeException(e);

		}
		return ds;
	}

	public Map<String, String> getDbInfo(String sourceDb) {
		String address = "spring.datasource.{0}.address";
		String username = "spring.datasource.{0}.username";
		String password = "spring.datasource.{0}.password";
		var c = "";
		if (!PropertyUtils.getPropertyCache(address.replace("{0}", sourceDb), "").isEmpty()) {
			c = sourceDb;
		} else if (!PropertyUtils.getPropertyCache(address.replace("{0}", removeNumber(sourceDb)), "").isEmpty()) {
			c = removeNumber(sourceDb);
		}
		if (c.isEmpty())
			throw new RuntimeException("数据库配置信息不存在:"+sourceDb);
		var map = StragtegyProvider.DB_MAPS.get(c);
		if (map != null) {
			return map;
		} else {
			map = new HashMap<String, String>();
			map.put("address", PropertyUtils.getPropertyCache(address.replace("{0}", c), ""));
			map.put("username", PropertyUtils.getPropertyCache(username.replace("{0}", c), ""));
			map.put("password", PropertyUtils.getPropertyCache(password.replace("{0}", c), ""));
			StragtegyProvider.DB_MAPS.put(c, map);
			return map;
		}

	}
	/**
	 * 传入db和table，对mysql和tidb同时有效
	 * @return
	 */
	public boolean isBlackTable(String targetDb, String targetTable) {
		if(BLACK_TABLES.size()==0){
			var value = PropertyUtils.getPropertyCache("black.tables", "");
			if (StringUtils.hasText(value)) {
				String[] tables = value.split(",");
				for (String temp : tables) {
					BLACK_TABLES.put(temp.split("\\.")[0].replace("*","") + "." + temp.split("\\.")[1].replace("*",""), "");
				}
			}
		}
		if (BLACK_TABLES.containsKey(targetDb + "." + targetTable)) {
			return true;
		} else if (BLACK_TABLES.containsKey(removeNumber(targetDb) + "." + targetTable)) {
			return true;
		} else if (BLACK_TABLES.containsKey(targetDb + "." + removeNumber(targetTable))) {
			return true;
		} else if (BLACK_TABLES.containsKey(removeNumber(targetDb) + "." + removeNumber(targetTable))) {
			return true;
		}
		return false;
	}

	public boolean isMysqlBlackTable(String sourceDb,String sourceTable){
		if(MysqlBLACK_TABLES.size()==0){
			var value = PropertyUtils.getPropertyCache("mysql.black.tables", "");
			if (StringUtils.hasText(value)) {
				String[] tables = value.split(",");
				for (String temp : tables) {
					MysqlBLACK_TABLES.put(temp.split("\\.")[0].replace("*","") + "." + temp.split("\\.")[1].replace("*",""), "");
				}
			}
		}
		if (MysqlBLACK_TABLES.containsKey(sourceDb + "." + sourceTable)) {
			return true;
		}
		return false;
	}
	/**
	 * 传入db和table，根据策略解析离线仓库对应的合并表
	 *
	 * @return
	 */
	public String getTable(String sourceDb, String sourceTable) {
		String stragtegy = "strategy.{0}.{1}";
		String value = PropertyUtils.getPropertyCache(stragtegy.replace("{0}", sourceDb).replace("{1}", sourceTable), "");
		if (StringUtils.hasText(value)) {
			return value;					
		}else {
			String target = PropertyUtils.getPropertyCache(stragtegy.replace("{0}", removeNumber(sourceDb)).replace("{1}", removeNumber(sourceTable)), "");
			if (StringUtils.hasText(target)) {
				return target;
			}			
			return sourceDb + "." + sourceTable;
		}
	}

	public String getTargetTable(String sourceDb, String sourceTable) {
		return getTable(sourceDb,sourceTable);
	}

	public String getTargetTable(String sourceDb, String sourceTable,boolean isTemp) {
		return getTable(sourceDb,sourceTable)+(isTemp?"_temp":"");
	}

	public String getDropTableName(String table){
		return table+"_DROP_"+ DateUtil.format(new Date(),"yyMMddHHmmss");
	}


	private  static String removeNumber(String str) {
		String removeNumStr= str.replaceAll("[^A-Z_a-z]", "");
		if(removeNumStr.endsWith("_")){
			int a=1;
		}
		return removeNumStr;
	}
}
